package Model.Gates;

import Model.Main.Wire;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 ** @author citromos ezustfoka
 */
public abstract class Gate {

    protected static final Logger logger =
            Logger.getLogger(Gate.class.getName());
    protected Wire outputWire=null;
    protected boolean val;
    protected ArrayList<Boolean> inputs;
    protected ArrayList<Boolean> inputsStable;
    protected int maxInputs;
    protected int maxOutputs;
    protected String name = null;
    private ArrayList<Boolean> inputsWired;

    /**
     *A bemenetek beállítása. Ezt hívja meg a wire
     * @param inputNumber Bemenet száma.
     * @param val Bemenet értéke
     * @return
     * 
     */
    public void setInput(int inputNumber, Boolean val) throws Exception {
        logger.log(Level.FINEST, "{0} {1} set value {2} on input {3}, and setting input to stable.",
                new Object[]{this.getClass().getName(), this.getName(), val, inputNumber});
        inputs.set(inputNumber, val);
        inputsStable.set(inputNumber, true);
    }

    /**
     * Az elem egy kimenetére csatlakoztatott vezeték beállítása.
     * @param wire A kimenetre kötni kívánt vezeték.
     * @param inputNumber Melyik kimenetre megy?
     * Gyermekosztály felüldefiniálja, ha több kimenete van.
     * @return
     */
    public void setOutputWire(Wire w, int inputNumber) {
        if (inputNumber != 0) {
            throw new UnsupportedOperationException();
        }
        this.outputWire = w;
    }

    /**
     * Minden alaphelyzetbe állítása.
     * @return
     *  
     */
    public void reset() {
        logger.log(Level.FINEST, "Resetting {0} {1} to default values.",
                new Object[]{this.getClass().getName(), this.getName()});
        inputs = new ArrayList<Boolean>();
        inputsStable = new ArrayList<Boolean>();
        val = false;
        maxOutputs = 1;
        // nem állhat kapu bemeneten wire nélkül, ezt majd később
        // most default 0-ra állítjuk a lábakat
        for (int i = 0; i < maxInputs; i++) {
            inputs.add(i, false);
            inputsStable.add(i, false);
        }
    }

    /**
     * Beállítja  a nevét az objektumnak.
     * @param newName
     */
    public void setName(String newName) {
        this.name = newName;
    }

    /**
     * Visszaadja a nevét az objektumnak
     * @return
     */
    public String getName() {
        if (this.name != null) {
            return this.name;
        }
        return "Undefined";
    }

    /**
     *
     * @param outputNumber
     * @return
     * @throws exception ha outputNumber != 0
     */
    public Wire getWire(int outputNumber) throws Exception {
        if (outputNumber != 0) {
            throw new Exception("There's only one leg on " + this);
        }
        return (outputWire);
    }

    public Boolean getVal() {
        return (val);
    }

    public int getMaxInput() {
        return (this.maxInputs);
    }

    public int getMaxOutput() {
        return (this.maxOutputs);
    }

    /**
     * Megkreálja az ellenőrzéshez szükséges listát
     * Azért fontos mert ezzel ellenőrizzük majd, 
     * hogy minden lábra van-e kötve valami
     * A gate leszármazottainak konstruktorában meg kell hívni,
     * különben elszáll a program!
     */
    protected void initInputsWired() {
        logger.log(Level.FINEST, "{0} creates empty InputsWired array!", this.getName());
        this.inputsWired = new ArrayList<Boolean>();
        for (int i = 0; i < maxInputs; i++) {
            inputsWired.add(i, false);
        }
    }

    /**
     * A fent leírt listában változtatja meg a szükséges elemet true-ra
     * Ezzel fejezzük ki hogy van valami az adott lábon.
     * @param number
     * @throws Exception nem jó lábszám
     */
    public void addWireToInput(int number) throws Exception {
        if (inputsWired.get(number) == true) {
            throw new Exception("There is a wire connected already to " + "input number " + number + " to " + this.getName() + ".");
        }
        if (inputsWired.get(number) == null) {
            throw new Exception("There is no such input on " + this.getName());
        }
        inputsWired.add(number, true);
    }

    /**
     * A fent leírt listában változtatja meg a szükséges elemet false-ra
     * Ezzel fejezzük ki, hogy nincs semmi az adott lábon.
     * @param number
     * @throws Exception
     */
    public void removeWireFromInput(int number) throws Exception {
        if (inputsWired.get(number) == false) {
            throw new Exception("There is no wire connected to " + "input number " + number + " to " + this.getName() + ".");
        }
        if (inputsWired.get(number) == null) {
            throw new Exception("There is no such input on " + this.getName());
        }
        inputsWired.add(number, false);
    }

    /**
     * Leteszteli, hogy van-e inicializálva InputsWired
     * összeéseli a tartalmát. ha van benne 1 false is false-t ad vissza.
     * @return
     */
    public Boolean testAllInputsWired() {
        if (inputsWired == null) {
            logger.log(Level.SEVERE, "inputsWired not initialized in " + this.getName());
            return false;
        }
        Boolean tv = true;
        for (int i = 0; i < maxInputs; i++) {
            tv = tv && inputsWired.get(i);
        }
        return tv;
    }

    protected void outputWireSendValue() throws Exception {
        // csak akkor küld adatot, ha van outputwire.
        if (outputWire != null) {
            outputWire.sendValue(this.val);
        } else {
            //ha nincs wire kivételt dobunk.
            throw new Exception("No wire on output to call on " + this.getName() + ".");
        }
    }
}
